#include "cgzCLua.h"
#include "cgzCHRedis.h"
#include "cgzCThreadPool.h"
#include <hiredis/hiredis.h>

void cgzCLua::redis2table(const redisReply &reply)
{
    switch (reply.type)
    {
    case REDIS_REPLY_STATUS:
    case REDIS_REPLY_ERROR:
    case REDIS_REPLY_STRING:
    {
        lua_pushlstring(L, reply.str, reply.len);
    }
    break;
    case REDIS_REPLY_INTEGER:
    {
        lua_pushinteger(L, reply.integer);
    }
    break;
    case REDIS_REPLY_NIL:
    {
        lua_pushnil(L);
    }
    break;
    case REDIS_REPLY_DOUBLE:
    {
        lua_pushnumber(L, reply.dval);
    }
    break;
    case REDIS_REPLY_BOOL:
    {
        lua_pushboolean(L, reply.integer);
    }
    break;
    case REDIS_REPLY_ARRAY:
    case REDIS_REPLY_MAP:
    case REDIS_REPLY_SET:
    {
        lua_cgz_newa(L, reply.elements);
        for (size_t i = 0; i < reply.elements; i++)
        {
            lua_pushinteger(L, i + 1);
            redis2table(*reply.element[i]);
            lua_settable(L, -3);
        }
    }
    break;
    default:
    {
        cgz_this_error("Error: redis reply type:{} not support", reply.type);
    }
    }
}

bool cgzCLua::run(const cgzITask *iTask, cgzCRedisReply *pnode)
{
    switch (pnode->m_code)
    {
    case cgzRedisCode::AUTH_SUCCESS:
    {
        ocall(iTask->m_tref, "auth", {});
    }
    break;
    case cgzRedisCode::EXEC_REPLY:
    {
        auto execReply = static_cast<cgzCRedisCmd *>(pnode);
        if (execReply->m_err)
        {
            ocall(iTask->m_tref, "reply", {execReply->m_cookid, false});
        }
        else
        {
            ocall(iTask->m_tref, "reply", {execReply->m_cookid, *(execReply->m_reply)});
        }
    }
    break;
    default:
    {
        cgz_this_error("Error: redis reply taskName:{} not support", iTask->getName());
        return false;
    }
    }
    return true;
}

static int hiredis_new(lua_State *L)
{
    // 将其转换为 cgzCLua 指针
    cgzCLua *that = cgzCLua::getThat(L);
    const char *name = lua_tostring(L, 1);
    const char *ip = lua_tostring(L, 2);
    int port = lua_tointeger(L, 3);
    int idb = lua_tointeger(L, 4);
    const char *auth = lua_tostring(L, 5);
    int tref = luaL_ref(L, LUA_REGISTRYINDEX);
    void *node = malloc(sizeof(cgzCHRedis));
    int cid = that->getId();
    auto cobj = new (node) cgzCHRedis(that->m_pool, name, ip, auth, port, idb, cid);
    cobj->m_tref = tref;
    return 0;
}

template <cgzRedisCmdType cmd>
static void hiredis_command(lua_State *L)
{
    cgzCLua *that = cgzCLua::getThat(L);
    long cookid = lua_tointeger(L, 1);
    const char *name = lua_tostring(L, 2);
    int top = lua_gettop(L);
    // 创建命令对象
    void *node = malloc(sizeof(cgzCRedisCmd));
    auto ask = new (node) cgzCRedisCmd(cmd, cookid);
    // 收集从第3个参数开始的所有参数
    size_t len = 0;
    for (int i = 3; i <= top; i++)
    {
        const char *str = lua_tolstring(L, i, &len);
        ask->m_args.emplace_back(str, len);
    }
    that->regTask(name, ask);
}

static int hiredis_select(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SELECT>(L);
    return 0;
}

static int hiredis_get(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::GET>(L);
    return 0;
}

static int hiredis_set(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SET>(L);
    return 0;
}

static int hiredis_del(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::DEL>(L);
    return 0;
}

static int hiredis_hdel(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::HDEL>(L);
    return 0;
}

static int hiredis_hexists(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::HEXISTS>(L);
    return 0;
}

static int hiredis_hrandfield(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::HRANDFIELD>(L);
    return 0;
}

static int hiredis_hstrlen(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::HSTRLEN>(L);
    return 0;
}

static int hiredis_hlen(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::HLEN>(L);
    return 0;
}

static int hiredis_hkeys(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::HKEYS>(L);
    return 0;
}

static int hiredis_hvals(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::HVALS>(L);
    return 0;
}

static int hiredis_hscan(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::HSCAN>(L);
    return 0;
}

static int hiredis_hincrby(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::HINCRBY>(L);
    return 0;
}

static int hiredis_hincrbyfloat(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::HINCRBYFLOAT>(L);
    return 0;
}

static int hiredis_hget(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::HGET>(L);
    return 0;
}

static int hiredis_hset(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::HSET>(L);
    return 0;
}

static int hiredis_hsetnx(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::HSETNX>(L);
    return 0;
}

static int hiredis_hmget(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::HMGET>(L);
    return 0;
}

static int hiredis_hmset(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::HMSET>(L);
    return 0;
}

static int hiredis_hgetall(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::HGETALL>(L);
    return 0;
}

static int hiredis_lpush(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::LPUSH>(L);
    return 0;
}

static int hiredis_rpush(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::RPUSH>(L);
    return 0;
}

static int hiredis_lpop(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::LPOP>(L);
    return 0;
}

static int hiredis_rpop(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::RPOP>(L);
    return 0;
}

static int hiredis_lrange(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::LRANGE>(L);
    return 0;
}

static int hiredis_ltrim(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::LTRIM>(L);
    return 0;
}

static int hiredis_sadd(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SADD>(L);
    return 0;
}

static int hiredis_srem(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SREM>(L);
    return 0;
}

static int hiredis_spop(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SPOP>(L);
    return 0;
}

static int hiredis_smove(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SMOVE>(L);
    return 0;
}

static int hiredis_scard(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SCARD>(L);
    return 0;
}

static int hiredis_sismember(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SISMEMBER>(L);
    return 0;
}

static int hiredis_smembers(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SMEMBERS>(L);
    return 0;
}

static int hiredis_srandmember(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SRANDMEMBER>(L);
    return 0;
}

static int hiredis_sdiff(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SDIFF>(L);
    return 0;
}

static int hiredis_sinter(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SINTER>(L);
    return 0;
}

static int hiredis_sunion(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SUNION>(L);
    return 0;
}

static int hiredis_sdiffstore(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SDIFFSTORE>(L);
    return 0;
}

static int hiredis_sinterstore(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SINTERSTORE>(L);
    return 0;
}

static int hiredis_sunionstore(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SUNIONSTORE>(L);
    return 0;
}

static int hiredis_zadd(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZADD>(L);
    return 0;
}

static int hiredis_zrem(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZREM>(L);
    return 0;
}

static int hiredis_zincrby(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZINCRBY>(L);
    return 0;
}

static int hiredis_zcard(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZCARD>(L);
    return 0;
}

static int hiredis_zrange(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZRANGE>(L);
    return 0;
}

static int hiredis_zrangebyscore(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZRANGEBYSCORE>(L);
    return 0;
}

static int hiredis_zrank(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZRANK>(L);
    return 0;
}

static int hiredis_zcount(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZCOUNT>(L);
    return 0;
}

static int hiredis_zscore(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZSCORE>(L);
    return 0;
}

static int hiredis_zremrangebyrank(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZREMRANGEBYRANK>(L);
    return 0;
}

static int hiredis_zremrangebyscore(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZREMRANGEBYSCORE>(L);
    return 0;
}

static int hiredis_zdiff(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZDIFF>(L);
    return 0;
}

static int hiredis_zinter(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZINTER>(L);
    return 0;
}

static int hiredis_zunion(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZUNION>(L);
    return 0;
}

static int hiredis_zdiffstore(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZDIFFSTORE>(L);
    return 0;
}

static int hiredis_zinterstore(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZINTERSTORE>(L);
    return 0;
}

static int hiredis_zunionstore(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::ZUNIONSTORE>(L);
    return 0;
}

static int hiredis_xadd(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::XADD>(L);
    return 0;
}
static int hiredis_xread(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::XREAD>(L);
    return 0;
}

static int hiredis_xgroup(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::XGROUP>(L);
    return 0;
}

static int hiredis_xreadgroup(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::XREADGROUP>(L);
    return 0;
}

static int hiredis_xack(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::XACK>(L);
    return 0;
}

static int hiredis_xinfo(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::XINFO>(L);
    return 0;
}

static int hiredis_xdel(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::XDEL>(L);
    return 0;
}

static int hiredis_xrange(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::XRANGE>(L);
    return 0;
}

static int hiredis_xrevrange(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::XREVRANGE>(L);
    return 0;
}

static int hiredis_xtrim(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::XTRIM>(L);
    return 0;
}

static int hiredis_xclaim(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::XCLAIM>(L);
    return 0;
}

static int hiredis_xpending(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::XPENDING>(L);
    return 0;
}

static int hiredis_xlen(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::XLEN>(L);
    return 0;
}

static int hiredis_ttl(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::TTL>(L);
    return 0;
}

static int hiredis_pttl(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::PTTL>(L);
    return 0;
}

static int hiredis_persist(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::PERSIST>(L);
    return 0;
}

static int hiredis_expire(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::EXPIRE>(L);
    return 0;
}

static int hiredis_pexpire(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::PEXPIRE>(L);
    return 0;
}

static int hiredis_expireat(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::EXPIREAT>(L);
    return 0;
}

static int hiredis_pexpireat(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::PEXPIREAT>(L);
    return 0;
}

static int hiredis_type(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::TYPE>(L);
    return 0;
}

static int hiredis_keys(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::KEYS>(L);
    return 0;
}

static int hiredis_randomkey(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::RANDOMKEY>(L);
    return 0;
}

static int hiredis_move(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::MOVE>(L);
    return 0;
}

static int hiredis_rename(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::RENAME>(L);
    return 0;
}

static int hiredis_sort(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SORT>(L);
    return 0;
}

static int hiredis_scan(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SCAN>(L);
    return 0;
}

static int hiredis_script(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::SCRIPT>(L);
    return 0;
}

static int hiredis_evalsha(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::EVALSHA>(L);
    return 0;
}

static int hiredis_eval(lua_State *L)
{
    hiredis_command<cgzRedisCmdType::EVAL>(L);
    return 0;
}

static int hiredis_callback(lua_State *L)
{
    // 将其转换为 cgzCLua 指针
    cgzCLua *that = cgzCLua::getThat(L);
    long cookid = lua_tointeger(L, 1);
    auto task = (cgzCRedisCmd *)that->m_retTask.back();
    assert(task->m_cookid == cookid);
    task->m_ret = true;
    return 0;
}

int cgzCLua::luaopen_hiredis(lua_State *L)
{
    static const luaL_Reg otherlib[] = {
        {"new", hiredis_new},
        {"select", hiredis_select},
        {"get", hiredis_get},
        {"set", hiredis_set},
        {"del", hiredis_del},
        {"hget", hiredis_hget},
        {"hmget", hiredis_hmget},
        {"hgetall", hiredis_hgetall},
        {"hset", hiredis_hset},
        {"hsetnx", hiredis_hsetnx},
        {"hincrby", hiredis_hincrby},
        {"hincrbyfloat", hiredis_hincrbyfloat},
        {"hmset", hiredis_hmset},
        {"hdel", hiredis_hdel},
        {"hlen", hiredis_hlen},
        {"hkeys", hiredis_hkeys},
        {"hvals", hiredis_hvals},
        {"hscan", hiredis_hscan},
        {"hexists", hiredis_hexists},
        {"hrandfield", hiredis_hrandfield},
        {"hstrlen", hiredis_hstrlen},
        {"lpush", hiredis_lpush},
        {"rpush", hiredis_rpush},
        {"lpop", hiredis_lpop},
        {"rpop", hiredis_rpop},
        {"lrange", hiredis_lrange},
        {"ltrim", hiredis_ltrim},
        {"sadd", hiredis_sadd},
        {"srem", hiredis_srem},
        {"spop", hiredis_spop},
        {"smove", hiredis_smove},
        {"scard", hiredis_scard},
        {"sismember", hiredis_sismember},
        {"smembers", hiredis_smembers},
        {"srandmember", hiredis_srandmember},
        {"sdiff", hiredis_sdiff},
        {"sinter", hiredis_sinter},
        {"sunion", hiredis_sunion},
        {"sdiffstore", hiredis_sdiffstore},
        {"sinterstore", hiredis_sinterstore},
        {"sunionstore", hiredis_sunionstore},
        {"zadd", hiredis_zadd},
        {"zrem", hiredis_zrem},
        {"zincrby", hiredis_zincrby},
        {"zcard", hiredis_zcard},
        {"zrange", hiredis_zrange},
        {"zrangebyscore", hiredis_zrangebyscore},
        {"zrank", hiredis_zrank},
        {"zcount", hiredis_zcount},
        {"zscore", hiredis_zscore},
        {"zremrangebyrank", hiredis_zremrangebyrank},
        {"zremrangebyscore", hiredis_zremrangebyscore},
        {"zdiff", hiredis_zdiff},
        {"zinter", hiredis_zinter},
        {"zunion", hiredis_zunion},
        {"zdiffstore", hiredis_zdiffstore},
        {"zinterstore", hiredis_zinterstore},
        {"zunionstore", hiredis_zunionstore},
        {"xadd", hiredis_xadd},
        {"xread", hiredis_xread},
        {"xgroup", hiredis_xgroup},
        {"xreadgroup", hiredis_xreadgroup},
        {"xack", hiredis_xack},
        {"xinfo", hiredis_xinfo},
        {"xdel", hiredis_xdel},
        {"xrange", hiredis_xrange},
        {"xrevrange", hiredis_xrevrange},
        {"xtrim", hiredis_xtrim},
        {"xclaim", hiredis_xclaim},
        {"xpending", hiredis_xpending},
        {"xlen", hiredis_xlen},
        {"ttl", hiredis_ttl},
        {"pttl", hiredis_pttl},
        {"persist", hiredis_persist},
        {"expire", hiredis_expire},
        {"pexpire", hiredis_pexpire},
        {"expireat", hiredis_expireat},
        {"pexpireat", hiredis_pexpireat},
        {"type", hiredis_type},
        {"keys", hiredis_keys},
        {"randomkey", hiredis_randomkey},
        {"move", hiredis_move},
        {"rename", hiredis_rename},
        {"sort", hiredis_sort},
        {"scan", hiredis_scan},
        {"script", hiredis_script},
        {"evalsha", hiredis_evalsha},
        {"eval", hiredis_eval},
        {"callback", hiredis_callback},
        {NULL, NULL}};

    luaL_newlib(L, otherlib);
    return 1;
}
